package dmmm.graph;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;

import javax.swing.*;

import dmmm.components.LogicalComponent;
import dmmm.components.Pin;
import dmmm.main.Main;
import dmmm.shemes.*;

public class TimeDiagram extends JFrame {
	// klasa sa prozorom za iscrtavanje vremenskih signala
	// moze se iscrtati vrednosti signala za deset signala koji se odaberu sa
	// strane(moze se odabrati i vise od deset ali oi posle desetog ce se
	// iscrtati samo ako se izbaci neki od prvih deset odabranih)
	// na pocetku se staticki vezuju ime signala i pin na osnovu koga se uzima
	// vrednost signala i to je u mapi mapSignPin
	// svaka vrednosts signala za ovih deset signala se iscrtava pomocu klase
	// DrawSquare kojih ima ukupno 10*10
	// u klasi SignalHistory se nalazi HashMapa(ime signala,arraylista vrednosti
	// signala od pocetka simulacije ili od takta kada je ucitana simulacija)

	private static final long serialVersionUID = 1L;
	private JPanel panMain, panEast, panCenter, panWest;
	private ArrayList<JLabel> labSignals;
	private ArrayList<JLabel> labCLK;
	private ArrayList<DrawSquare[]> arraySquares;
	private static HashMap<String, Pin> mapSignPin = new HashMap<String, Pin>();
	private static SignalsHistory history = new SignalsHistory();;
	private static int currentFrame=0;
	private ArrayList<JCheckBox> allChecks;
	private LinkedList<String> checked;

	public TimeDiagram() {
		super("Vremenski dijagram");
		setLocation(100, 100);
		// Logic inside
		currentFrame = 0;
		allChecks = new ArrayList<JCheckBox>();
		checked = new LinkedList<String>();

		TimeDiagram.addSignals();// da se povezu pinovi sa nazivima signala

		// CENTER
		arraySquares = new ArrayList<DrawSquare[]>(10);
		panCenter = new JPanel();
		panCenter.setSize(500, 550);
		panCenter.setLayout(new GridLayout(11, 10));
		labCLK = new ArrayList<JLabel>();
		for (int i = 0; i < 10; i++) {
			JLabel l = new JLabel();
			l.setHorizontalAlignment(SwingConstants.CENTER);
			l.setText("-");
			labCLK.add(l);
			panCenter.add(l);
		}
		// make drawSquare for that signal
		for (int i = 0; i < 10; i++) {
			DrawSquare[] dsq = new DrawSquare[10];
			for (int j = 0; j < 10; j++) {
				DrawSquare ds = new DrawSquare();
				dsq[j] = ds;
				panCenter.add(ds);
			}
			arraySquares.add(i, dsq);
		}

		// WEST
		panWest = new JPanel();
		panWest.setSize(100, 550);
		panWest.setMinimumSize(new Dimension(100, 550));
		panWest.setPreferredSize(new Dimension(100, 550));
		panWest.setLayout(new GridLayout(11, 1));
		JLabel j = new JLabel("Clock->");
		j.setHorizontalAlignment(SwingConstants.CENTER);
		panWest.add(j);
		labSignals = new ArrayList<JLabel>();
		for (int i = 0; i < 10; i++) {
			String s = "signal" + i + "      ";
			JLabel l = new JLabel(s);
			l.setHorizontalAlignment(SwingConstants.CENTER);
			labSignals.add(l);
			panWest.add(l);
		}
		// EAST
		panEast = new JPanel(new BorderLayout());
		JPanel signals = new JPanel(new BorderLayout());
		signals.setBackground(new java.awt.Color(255, 255, 255));
		signals.setBorder(BorderFactory
				.createTitledBorder("Odabir signala za prikaz"));

		JPanel panScroll = new JPanel();// ovaj i naredni red sluze za
										// automatsko skrolovanje velikih
										// komponenti
		panScroll.setLayout(new GridLayout(74, 2));
		// panScroll.setViewportView(drawTable);
		// panScroll.add(drawTable,ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		SortedSet<String> keys = new TreeSet<String>(mapSignPin.keySet());
		for (String sgn : keys) {
			JCheckBox c = new JCheckBox();
			c.setText(sgn);
			allChecks.add(c);
			panScroll.add(c);
		}
		// signals.add(panScroll);
		JScrollPane temp = new JScrollPane(panScroll);
		signals.add(temp);

		JButton drawButton = new JButton("Draw checked signals");
		drawButton.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				drawCheckedAction();

			}
		});
		signals.add("South", drawButton);

		panEast.add("Center", signals);

		JPanel navigation = new JPanel(new GridLayout(1, 2));
		JButton leftFrame = new JButton("Left Frame");
		leftFrame.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				leftFrameAction();

			}
		});
		navigation.add(leftFrame);
		JButton rightFrame = new JButton("Right Frame");
		rightFrame.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				rightFrameAction();

			}
		});
		navigation.add(rightFrame);
		panEast.add("South", navigation);

		// MAIN FRAME
		panMain = new JPanel(new BorderLayout());
		panMain.setSize(800, 550);
		panMain.add(BorderLayout.WEST, panWest);
		panMain.add(BorderLayout.CENTER, panCenter);
		panMain.add(BorderLayout.EAST, panEast);
		add(panMain);
		setSize(800, 550);
		setMinimumSize(new Dimension(800, 550));
		setPreferredSize(new Dimension(800, 550));
		setResizable(false);
		validate();
		setVisible(false);
		
	}

	public static void addSignals() {
		mapSignPin.put("ldMAR", Oper1.ldMAR());
		mapSignPin.put("incMAR", Oper1.incMAR());
		mapSignPin.put("mxMDR", Oper1.mxMDR());
		mapSignPin.put("ldMDR", Oper1.ldMDR());
		mapSignPin.put("MDRout2", Oper1.MDRout2());
		mapSignPin.put("eMAR", Oper1.eMAR());
		mapSignPin.put("eMDR", Oper1.eMDR());
		mapSignPin.put("wrCPU", Oper1.wrCPU());
		mapSignPin.put("rdCPU", Oper1.rdCPU());
		mapSignPin.put("ldDWH", Oper1.ldDWH());
		mapSignPin.put("ldDWL", Oper1.ldDWL());
		mapSignPin.put("DWout1", Oper1.DWout1());
		mapSignPin.put("MOST1_2", Oper1.MOST1_2());
		mapSignPin.put("MOST2_1", Oper1.MOST2_1());
		mapSignPin.put("ldPC", Oper1.ldPC());
		mapSignPin.put("PCout2", Oper1.PCout2());
		mapSignPin.put("PCHout2", Oper1.PCHout2());
		mapSignPin.put("ldIR0", Oper1.ldIR0());
		mapSignPin.put("ldIR1", Oper1.ldIR1());
		mapSignPin.put("ldIR2", Oper1.ldIR2());
		mapSignPin.put("ldIR3", Oper1.ldIR3());
		mapSignPin.put("IRJAout1", Oper1.IRJAout1());
		mapSignPin.put("IRDAout1", Oper1.IRDAout1());
		mapSignPin.put("IRPOMout1", Oper1.IRPOMout1());
		mapSignPin.put("IRBRout1", Oper1.IRBRout1());
		mapSignPin.put("ldGPRAR", Oper1.ldGPRAR());
		mapSignPin.put("incGPRAR", Oper1.incGPRAR());
		mapSignPin.put("GPRout2", Oper1.wrGPR());
		mapSignPin.put("ldSP", Oper1.ldSP());
		mapSignPin.put("incSP", Oper1.incSP());
		mapSignPin.put("decSP", Oper1.decSP());
		mapSignPin.put("SPout1", Oper1.SPout1());
		mapSignPin.put("ldCW", Oper1.ldCW());
		mapSignPin.put("ADDout1", Oper1.ADDout1());
		mapSignPin.put("ldAB", Oper1.ldAB());
		mapSignPin.put("ABout2", Oper1.ABout2());
		mapSignPin.put("shr", Oper1.shr());
		mapSignPin.put("shl", Oper1.shl());
		mapSignPin.put("ldBB", Oper1.ldBB());
		mapSignPin.put("BBout2", Oper1.BBout2());
		mapSignPin.put("ldAW", Oper1.ldAW());
		mapSignPin.put("AWout2", Oper1.AWout2());
		mapSignPin.put("AWHout2", Oper1.AWHout2());
		mapSignPin.put("ldBW", Oper1.ldBW());
		mapSignPin.put("BWout1", Oper1.BWout1());
		mapSignPin.put("ALUout1", Oper1.ALUout1());
		mapSignPin.put("stPSWI", Oper1.stPSWI());
		mapSignPin.put("clPSWI", Oper1.clPSWI());
		mapSignPin.put("stPSWT", Oper1.stPSWT());
		mapSignPin.put("clPSWT", Oper1.clPSWT());
		mapSignPin.put("ldL", Oper1.ldL());
		mapSignPin.put("ldN", Oper1.ldN());
		mapSignPin.put("ldZ", Oper1.ldZ());
		mapSignPin.put("ldC", Oper1.ldC());
		mapSignPin.put("ldV", Oper1.ldV());
		mapSignPin.put("ldPSWL", Oper1.ldPSWL());
		mapSignPin.put("ldPSWH", Oper1.ldPSWH());
		mapSignPin.put("PSWHout2", Oper1.PSWHout2());
		mapSignPin.put("PSWLout2", Oper1.PSWLout2());
		mapSignPin.put("clSTART", Oper1.clSTART());
		mapSignPin.put("add", Oper1.add());
		mapSignPin.put("sub", Oper1.sub());
		mapSignPin.put("inc", Oper1.inc());
		mapSignPin.put("dec", Oper1.dec());
		mapSignPin.put("and", Oper1.and());
		mapSignPin.put("or", Oper1.or());
		mapSignPin.put("xor", Oper1.xor());
		mapSignPin.put("not", Oper1.not());
		mapSignPin.put("stPRINS", Oper1.stPRINS());
		mapSignPin.put("clPRINS", Oper1.clPRINS());
		mapSignPin.put("stPRCOD", Oper1.stPRCOD());
		mapSignPin.put("clPRCOD", Oper1.clPRCOD());
		mapSignPin.put("stPRADR", Oper1.stPRADR());
		mapSignPin.put("clPRADR", Oper1.clPRADR());
		mapSignPin.put("clPRINM", Oper1.clPRINM());
		mapSignPin.put("clINTR", Oper1.clINTR());
		mapSignPin.put("ldIMR", Oper1.ldIMR());
		mapSignPin.put("IMRout1", Oper1.IMRout1());
		mapSignPin.put("ldIVTP", Oper1.ldIVTP());
		mapSignPin.put("IVTPout1", Oper1.IVTPout1());
		mapSignPin.put("ldBR", Oper1.ldBR());
		mapSignPin.put("IVTDSPout2", Oper1.IVTDSPout2());
		mapSignPin.put("UINTout1", Oper1.UINTout1());
		mapSignPin.put("UEXTout1", Oper1.UEXTout1());
		mapSignPin.put("bradr", Uprav1.bradr());
		mapSignPin.put("branch", Uprav1.branch());
		mapSignPin.put("bropr", Uprav1.bropr());
		mapSignPin.put("rdMEM", Mem1.rdMEM());
		mapSignPin.put("wrMEM", Mem1.wrMEM());
		mapSignPin.put("wrMEM", Mem1.wrMEM());
		mapSignPin.put("fcMEM", Mem2.fcMEM());
		mapSignPin.put("MEMout", Mem2.MEMout());
		mapSignPin.put("RDBUS\\", Bus1.NOTRDBUS());
		mapSignPin.put("WRBUS\\", Bus1.NOTWRBUS());
		mapSignPin.put("FCBUS\\", Bus1.NOTFCBUS());
		mapSignPin.put("N", Exec3.N());
		mapSignPin.put("Z", Exec3.Z());
		mapSignPin.put("C", Exec3.C());
		mapSignPin.put("V", Exec3.V());
		mapSignPin.put("gropr", Fetch3.gropr());
		mapSignPin.put("gradr", Fetch3.gradr());
		mapSignPin.put("I1", Fetch3.I1());
		mapSignPin.put("I2_brnch", Fetch3.I2_brnch());
		mapSignPin.put("I2_arlog", Fetch3.I2_arlog());
		mapSignPin.put("I3_jump", Fetch3.I3_jump());
		mapSignPin.put("store", Fetch3.store());
		mapSignPin.put("I3_arlog", Fetch3.I3_arlog());
		mapSignPin.put("HALT", Fetch2.HALT());
		mapSignPin.put("NOP", Fetch2.NOP());
		mapSignPin.put("RTI", Fetch2.RTI());
		mapSignPin.put("INT", Fetch2.INT());
		mapSignPin.put("RTS", Fetch2.RTS());
		mapSignPin.put("JSR", Fetch2.JSR());
		mapSignPin.put("JMP", Fetch2.JMP());
		mapSignPin.put("PUSHW", Fetch2.PUSHW());
		mapSignPin.put("PUSHB", Fetch2.PUSHB());
		mapSignPin.put("POPW", Fetch2.POPW());
		mapSignPin.put("POPB", Fetch2.POPB());
		mapSignPin.put("STW", Fetch2.STW());
		mapSignPin.put("STB", Fetch2.STB());
		mapSignPin.put("LDW", Fetch2.LDW());
		mapSignPin.put("LDB", Fetch2.LDB());
		mapSignPin.put("ROLC", Fetch2.ROLC());
		mapSignPin.put("ROL", Fetch2.ROL());
		mapSignPin.put("LSL", Fetch2.LSL());
		mapSignPin.put("ASL", Fetch2.ASL());
		mapSignPin.put("RORC", Fetch2.RORC());
		mapSignPin.put("ROR", Fetch2.ROR());
		mapSignPin.put("LSR", Fetch2.LSR());
		mapSignPin.put("ASR", Fetch2.ASR());
		mapSignPin.put("STSP", Fetch2.STSP());
		mapSignPin.put("LDSP", Fetch2.LDSP());
		mapSignPin.put("STIMR", Fetch2.STIMR());
		mapSignPin.put("LDIMR", Fetch2.LDIMR());
		mapSignPin.put("STIVTP", Fetch2.STIVTP());
		mapSignPin.put("LDIVTP", Fetch2.LDIVTP());
		mapSignPin.put("imm", Fetch2.imm());
		mapSignPin.put("pcpom", Fetch2.pcpom());
		mapSignPin.put("bxpom", Fetch2.bxpom());
		mapSignPin.put("regindpom", Fetch2.regindpom());
		mapSignPin.put("memind", Fetch2.memind());
		mapSignPin.put("memdir", Fetch2.memdir());
		mapSignPin.put("regind", Fetch2.regind());
		mapSignPin.put("regdir", Fetch2.regdir());
		mapSignPin.put("ABUS", Bus1.ABUS());
		mapSignPin.put("DBUS", Bus1.DBUS());
		mapSignPin.put("IBUS1",Bus1.IBUS1());
		mapSignPin.put("IBUS2",Bus1.IBUS2());
		

		for (String sign : mapSignPin.keySet()) {// puts all remembered signals
													// in history
			history.putSignal(sign);
		}
	}

	// na svaki logicalComponent.CLK(tj. u CLK+INS+,PRG+,GOTO+) se poziva ova
	// funkcija kako bi za svaki signal koji se iscrtava
	// zapamtila vrednost signala u istoriju tj dodala u listu vrednosti svakog
	// signala koji se pamti kroz istoriju
	public static void putInSignalsHistory() {
		for (String sgn : mapSignPin.keySet()) {
			Pin pinOfSignal = mapSignPin.get(sgn);
			ValueOfSignal val = new ValueOfSignal();
			val.setIsHighZ(pinOfSignal.isHighZ());
			val.setIsBool(pinOfSignal.isBool());
			val.setBoolValue(pinOfSignal.getBoolVal());
			val.setIntValue(pinOfSignal.getIntVal());
			history.getHistoryForSignal(sgn).add(val);
		}

	}

	// na svaki CLK-,INS-,GOTO- se poziva da se obrisu vrednosti iz istorije
	public static void removeEndOfHistory() {
		int newClock = LogicalComponent.globalClock;// klok na kome se nalazi
													// simulacija nakon vrecanja
													// unazad
		for (String sgn : mapSignPin.keySet()) {
			ArrayList<ValueOfSignal> oneHistory = history
					.getHistoryForSignal(sgn);
			// zbog onog u nula sto pamti vrednosti signala,npr u kloku 0 ima 1
			// vrenost,u kloku 5 ima 6 vrednosti
			// int r=oneHistory.size()-(newClock+1);
			// for(int i=0;i<r;i++){
			// oneHistory.remove(oneHistory.size()-1);//remove "r" last history
			// for each signal
			// }

			// sve dok ima viskova oni se uklanjaju sa kraja istorije vrednosti
			// zbog vracanja simulacije unazad
			while (oneHistory.size() > (newClock + 1)) {
				oneHistory.remove(oneHistory.size() - 1);
			}

		}
	}

	// desava se kad se stisne dugme reset da se izbrise cela istorija signala
	public static void resetAllHistory() {
		history.resetAllHistory();
		currentFrame=0;
	}

	public void checkChecked() {
		for (JCheckBox c : allChecks) {
			String sign = c.getText();
			if (c.isSelected()) {
				if (!checked.contains(sign)) {
					checked.addLast(sign);
				}
			} else {
				if (checked.contains(sign)) {
					checked.remove(sign);
				}
			}
		}
	}

	public void drawSignalsNames() {
		int i = 0;
		for (String sign : checked) {
			labSignals.get(i).setText(sign);
			i++;
			if (i == 10)
				break;// exit after drew ten names
		}
		if (i < 10) {// reset labels if before were named
			for (; i < 10; i++) {
				labSignals.get(i).setText("signal" + i + "      ");
			}
		}

	}

	public void drawCLK() {
		int i = 0;
		int maxCLK = LogicalComponent.globalClock;
		if ((currentFrame * 10 + i) <= maxCLK) {
			for (JLabel lab : labCLK) {
				lab.setText((currentFrame * 10 + i) + "");
				i++;
				if ((currentFrame * 10 + i) > maxCLK)
					break;// exit after drew ten names
			}
			if (i < 10) {// reset labels if before were used
				for (; i < 10; i++) {
					labCLK.get(i).setText("-");
				}
			}

		} else {
			for (JLabel lab : labCLK) {
				lab.setText("-");
			}
		}
	}

	public void drawSignalsValues() {
		int i = 0;
		for (String sign : checked) {// za svaki signal koji je obelezen
			ArrayList<ValueOfSignal> oneHistory = history
					.getHistoryForSignal(sign);
			int start = currentFrame * 10;
			int j = 0;
			// uspravne linije se kreiraju na osnovu prethodne vrednosti i zato
			// je ona potrebna
			ValueOfSignal beforeValue = ((start + j) <= 0) ? oneHistory.get(0)
					: oneHistory.get(start + j - 1);
			ValueOfSignal value;

			for (; j < 10;) {// za iscrtavanje detet vrednosti svakog chekiranog
								// signala ako one postoje
				value = oneHistory.get(start + j);
				DrawSquare square = arraySquares.get(i)[j];// panel na kome se
															// iscrtava tekuca
															// vrednost obelezenog signala
				square.resetAllLines();//obrisati prethodna iscrtavanja

				if(value.getIsHighZ()){
					if(beforeValue.getIsHighZ()){//i prethodna vrednost bila visoka impedansa
						square.setMid(true);
					}else{//prethodna vrednost nije bila impendansa
						square.setMid(true);
						if(beforeValue.getIsBool()){//u pitanju je boolean vrednost signala
							if(beforeValue.getBoolValue()){
								square.setVertH(true);//samo gornja polovina vertikalne se aktivira
							}else{
								square.setVertL(true);//samo donja polovina vertikalne se aktivira
							}
						}else{//signal je integer tipa bio pre ove vrenosti znaci ide cela vertikalan linija
							square.setVert(true);
						}
					}
				}else{//tekuca vrenost signala nije impedansa
					if(value.getIsBool()){//u pitanju je boolean signal
						if(beforeValue.getIsHighZ()){//ako je prethodni signal bio impedansa
							if(value.getBoolValue()){//crta se gornja polovina vertikalne linije
								square.setVertH(true);
							}else{//crta se donja polovina vertikalne linije
								square.setVertL(true);
							}	
						}else{//prethondi signal imao vrednost koja nije impedansa
							if(beforeValue.getBoolValue()!=value.getBoolValue()){//mora da ima vertikalna linija
								square.setVert(true);
							}
						}
						if(value.getBoolValue()){//crta se gornja linija
							square.setUp(true);
						}else{//crta se donja linija
							square.setDown(true);
						}	
					}else{//u pitanju je integer signal
						if(beforeValue.getIsHighZ()){//ako je prethodni signal bio impedansa,sad vertikalna linija da se podigne
							square.setVert(true);
						}
						square.setUp(true);//gornja i donja linija se crtaju
						square.setDown(true);
						//mora da mu se postavi vrednost koju ispisuje
						square.setPrintHexValue(true);
						String valueS;
						if("DBUS".equals(sign))
							valueS = String.format("%02x", value.getIntValue());
						else//za ABUS i IBUS1 i 2
							valueS = String.format("%04x", value.getIntValue());
						square.setHexValue(valueS);
					}
				}
				
				beforeValue = value;
				j++;
				if (start + j > LogicalComponent.globalClock) {
					break;
				}
			}
			if (j < 10) {// ako sam isao unazad CLK,onda neke vrednosti treba da
							// se poniste iz iscrtavanja
				for (; j < 10; j++) {
					arraySquares.get(i)[j].resetAllLines();
				}
			}

			i++;
			if (i == 10)
				break;// izlazi se kada se iscrtaju vrednosti za vise od 10
						// signala
		}
		if (i < 10) {// vrednosti za signale koji nisu iscrtani se brisu sa
						// dijagrama
						// jer (i) je broj signala za koji se iscrtavaju
						// vrednosti
			for (int j = 0; j < 10; j++) {
				arraySquares.get(i)[j].resetAllLines();
			}
		}
		for (DrawSquare[] dsq : arraySquares) {
			for (int k = 0; k < 10; k++) {
				dsq[k].repaint();
			}
		}
	}

	public void drawCheckedAction() {
		checkChecked();
		drawSignalsNames();
		drawCLK();
		drawSignalsValues();
	}

	public void leftFrameAction() {
		if (currentFrame > 0) {
			currentFrame--;
			drawSignalsNames();
			drawCLK();
			drawSignalsValues();
		}
	}

	public void rightFrameAction() {
		int maxCLK = LogicalComponent.globalClock;
		int max = (currentFrame + 1) * 10;
		if (max <= maxCLK) {
			currentFrame++;
			drawSignalsNames();
			drawCLK();
			drawSignalsValues();
		}
	}

	public void updateTimeDiagramView() {
		drawSignalsNames();
		drawCLK();
		drawSignalsValues();
	}

	public static void main(String s[]) {
		new TimeDiagram().setVisible(true);

	}
}
